package blockchain

import (
	"bytes"
	"crypto/sha256"
	"encoding/gob"
	"goblockchain/transaction"
	"goblockchain/utils"
	"time"
)

// block: basic info + txs
type Block struct {
	Timestamp int64
	Hash      []byte
	PrevHash  []byte
	Target    []byte
	Nonce     int64
	// Data      []byte
	Transactions []*transaction.Transaction // real data
}

// join all tx-hash(ID) into a []byte for Hash()
func (b *Block) BackTrasactionSummary() []byte {
	txIDs := make([][]byte, 0)
	for _, tx := range b.Transactions {
		txIDs = append(txIDs, tx.ID)
	}
	summary := bytes.Join(txIDs, []byte{})
	return summary
}

// compute and set block hash
func (b *Block) SetHash() {
	information := bytes.Join(
		[][]byte{
			utils.ToHexInt(b.Timestamp),
			b.PrevHash,
			utils.ToHexInt(b.Nonce),
			b.Target,
			b.BackTrasactionSummary(),
		},
		[]byte{},
	)
	hash := sha256.Sum256(information)
	b.Hash = hash[:]
}

// create new block
func CreateBlock(prevhash []byte, txs []*transaction.Transaction) *Block {
	block := Block{
		Timestamp:    time.Now().Unix(),
		Hash:         []byte{},
		PrevHash:     prevhash,
		Target:       []byte{},
		Nonce:        0,
		Transactions: txs,
	}
	// compute target and hash
	block.Target = block.GetTarget()
	block.Nonce = block.FindNonce()
	block.SetHash()
	return &block
}

// create genesis-block
func GenesisBlock(addr []byte) *Block {
	// make a base-tx: generate init coin for the God
	tx := transaction.BaseTx(addr)
	block := CreateBlock([]byte("base"), []*transaction.Transaction{tx})
	block.SetHash()
	return block
}

// Serialize and Deserialize
func (b *Block) Serialize() []byte{
	var res bytes.Buffer
	encoder := gob.NewEncoder(&res)
	err := encoder.Encode(b)
	utils.Handle(err)
	return res.Bytes()
}
func DeSerialize(data []byte) *Block{
	var block Block
	decoder := gob.NewDecoder(bytes.NewReader(data))
	err := decoder.Decode(&block)
	utils.Handle(err)
	return &block
}